<script setup lang='ts'>
import { wktToGeoJSON } from "@terraformer/wkt";
import { ElMessage } from "element-plus";
import { Map } from "ol";
import * as olExtent from "ol/extent";
import { GeoJSON } from "ol/format";
import { defaults as defaultInteractions } from "ol/interaction";
import { Vector as VectorLayer } from "ol/layer";
import { Vector as VectorSource } from "ol/source";
import { Fill, Stroke, Style } from "ol/style";
import { onMounted, onUnmounted, ref } from "vue";
import Search from "~icons/ep/search";
import { mapApi } from "@/api";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { exitFullScreen, fullScreen } from "@/utils/tools";
import setControl from "./utils/control";
import { fixedColors, layersOfVector, tk } from "./utils/layer";
import { addMarker, clearMarkers, destroyMarker, initMarker, isMarking, startMarking, stopMarking } from "./utils/marker";
import { clearMeasure, destroyMeasure, drawBoundary, editMeasure, initMeasure, isMeasuring, measureType, startMeasure, stopMeasure } from "./utils/measure";
import { dataProjection, projection, view } from "./utils/view";

defineOptions({
    name: "OpenLayers",
});

// #region ================================== 地图相关 ==================================

const openlayersMap = ref<HTMLDivElement>(); // 地图容器引用
const map = ref<Map | null>(); // OpenLayers地图实例
const controls = ["zoom", "zoomSlider", "mousePosition", "overviewMap", "scale"]; // 控件数组

const initMap = () => {
    if (map.value) destroyMap(); // 如果地图已存在，先销毁
    map.value = new Map({
        target: openlayersMap.value, // 地图容器ID
        controls: [], // 禁用默认控件, 使用自定义的控件
        interactions: defaultInteractions({
            altShiftDragRotate: true, // 启用 Alt+Shift 拖拽旋转, 默认启用
            pinchRotate: true, // 启用双指触屏旋转, 默认启用
        }),
        layers: layersOfVector, // 添加天地图矢量图层
        view,
    });

    setControl(map.value, controls); // 设置地图控件
    initMeasure(map.value); // 初始化测量工具
    initMarker(map.value); // 初始化打点

    // 行政区划编码文档: http://lbs.tianditu.gov.cn/server/administrative2.html
    mapApi.getAdministrativeInfo({ tk, keyword: "156150000" }).then((resp) => {
        if (resp.status === 200 && resp.data.district) {
            resp.data.district.forEach((item, idx) => {
                /**
                 * 将WKT格式转换为GeoJSON,这里使用JSON.stringify是为了保持类型一致, 便于后续处理
                 * 关于wkt格式: https://www.cnblogs.com/zhupanpan/p/14032149.html
                 */
                const geoJson = JSON.stringify(wktToGeoJSON(item.boundary));
                adjustView(geoJson); // 调整地图视图到行政区划边界
                if (!item.children) {
                    drawGeoJson(geoJson, idx);
                } else {
                    const promiseArr = item.children.map((child) => {
                        return new Promise<string>((resolve, reject) => {
                            mapApi.getAdministrativeInfo({ tk, keyword: child.gb }).then((childResp) => {
                                if (childResp.status === 200 && childResp.data.district) {
                                    const geoJson = JSON.stringify(wktToGeoJSON(childResp.data.district[0].boundary));
                                    resolve(geoJson);
                                } else {
                                    console.error("获取子行政区划数据失败", childResp);
                                    reject(childResp);
                                }
                            });
                        });
                    });
                    Promise.all(promiseArr).then((geoJsonArr) => {
                        geoJsonArr.forEach((geoJson, idx) => {
                            drawGeoJson(geoJson, idx); // 添加子行政区划边界
                        });
                    });
                }
            });
        } else {
            console.error("获取行政区划数据失败", resp);
        }
    });
};

/** 将边界信息绘制到地图上 */
const drawGeoJson = (geoJson: string, index: number) => {
    if (!map.value) {
        console.error("地图未初始化");
        return;
    };

    // 将后端接口投影转换成正确的视图投影后获取 features
    const features = new GeoJSON({ dataProjection, featureProjection: projection }).readFeatures(geoJson);
    const vectorSource = new VectorSource({ features });

    const lineLayer = new VectorLayer({
        zIndex: 999, // 确保边界线在最上层
        source: vectorSource,
        style: styleFunction(index),
    });

    // 添加边界线图层到地图
    map.value.addLayer(lineLayer);
};

/** 缓存地图边界范围，避免重复计算 */
let extendCache: olExtent.Extent | null = null;

/** 根据geoJson调整地图中心点和缩放 */
const adjustView = (geoJson?: string) => {
    if (!extendCache) {
        if (!geoJson) {
            console.error("没有提供geoJson数据, 无法调整视图");
            return;
        }
        // 将后端接口投影转换成正确的视图投影后获取 features
        const features = new GeoJSON({ dataProjection, featureProjection: projection }).readFeatures(geoJson);
        const extent = olExtent.createEmpty();
        features.forEach((feature) => {
            olExtent.extend(extent, feature.getGeometry()!.getExtent());
        });
        extendCache = extent;
    }
    const mapView = map.value?.getView();
    mapView?.fit(extendCache, { padding: [10, 10, 10, 10] });
    mapView?.setCenter(olExtent.getCenter(extendCache));
};

/** 设置边界样式 */
const styleFunction = (index: number): Style => {
    const fillColor = `${fixedColors[index % fixedColors.length]}10`;
    return new Style({
        fill: new Fill({
            color: fillColor,
        }),
        stroke: new Stroke({
            color: [49, 160, 210, 10],
            width: 2, // 边界线宽度
        }),
    });
};

/** 重置地图旋转 */
const resetRotation = () => {
    if (map.value) {
        const view = map.value.getView();
        view.setRotation(0); // 重置地图旋转角度
        // 设置动画
        view.animate({
            duration: 1000, // 动画持续时间
            easing: t => t, // 线性动画
        });
    }
};

/** 销毁地图 */
const destroyMap = () => {
    destroyMeasure(); // 清除测量工具
    destroyMarker(); // 清除标记工具
    extendCache = null; // 清除边界范围缓存
    map.value?.setTarget(); // 清除目标元素
    map.value?.getLayers().clear(); // 清除所有图层
    map.value?.dispose(); // 销毁地图实例
    map.value = null; // 清空引用
};

// #endregion =============================== 地图相关 ==================================
// #region ================================== 全屏相关 ==================================

const openLayersWrapper = ref<HTMLDivElement | null>(null);
const isFullScreen = ref<boolean>(false);

/** 切换全屏状态 */
const toggleFullScreen = () => {
    if (isFullScreen.value) {
        exitFullScreen().then(() => {
            isFullScreen.value = false;
        });
    } else {
        fullScreen(openLayersWrapper.value).then(() => {
            isFullScreen.value = true;
        });
    }
};

// #endregion =============================== 全屏相关 ==================================
// #region ================================== 地名搜索 ==================================

const searchKeyword = ref<string>(""); // 搜索关键词
const searchResults = ref<any>(); // 搜索结果, 根据需要确定类型
const isSearching = ref<boolean>(false); // 搜索状态

const getSearchPlace = (keyword: string): void => {
    if (!keyword.trim()) {
        searchResults.value = null;
        ElMessage.warning("请输入搜索关键词");
        return;
    }

    isSearching.value = true;
    mapApi.searchPlace(keyword, tk).then((resp) => {
        if (resp.status === "0") {
            clearMarkers(); // 清除之前的标记点
            searchResults.value = resp.location;
            const lonLat: [number, number] = [Number(resp.location.lon), Number(resp.location.lat)];
            addMarker(lonLat); // 添加搜索结果标记点
            centerMap(lonLat); // 定位到搜索结果中心点
        } else {
            searchResults.value = null;
            ElMessage.warning("未找到相关地点");
        }
    }).finally(() => {
        isSearching.value = false;
    });
};

// 定位到中心点
const centerMap = (lonLat: [number, number]): void => {
    if (!map.value) return;
    const view = map.value.getView();
    view.setCenter(lonLat);
    view.setZoom(14); // 设置合适的缩放级别
};

// #endregion =============================== 地名搜索 ==================================

onMounted(() => {
    initMap(); // 初始化地图
});

onUnmounted(() => {
    destroyMap(); // 清理地图资源
});
</script>

<template>
    <div ref="openLayersWrapper" class="openLayersWrapper">
        <!-- OpenLayers地图容器 -->
        <div ref="openlayersMap" class="h-full" />
        <div class="input-with-select absolute! top-[20px] right-[130px] z-99 bg-rounded-base">
            <el-input
                v-model="searchKeyword"
                style="max-width: 300px"
                placeholder="搜索地址"
                @keyup.enter="getSearchPlace(searchKeyword)"
            >
                <template #append>
                    <el-button :icon="useRenderIcon(Search)" @click="getSearchPlace(searchKeyword)" />
                </template>
            </el-input>
        </div>
        <!-- teleported设置为false，不将下拉列表插入至 body 元素，防止全屏时候不能下拉 -->
        <div class="absolute! top-[20px] right-[10px] z-99 bg-rounded-base">
            <el-dropdown trigger="click" :teleported="false">
                <el-button>
                    更多功能
                    <el-icon class="el-icon--right">
                        <span class="icon-[ant-design--down-outlined]" />
                    </el-icon>
                </el-button>
                <template #dropdown>
                    <el-dropdown-menu>
                        <el-dropdown-item @click="toggleFullScreen()">
                            <el-icon class="el-icon--left">
                                <span v-show="!isFullScreen" class="icon-[ant-design--fullscreen-outlined]" />
                                <span v-show="isFullScreen" class="icon-[ant-design--fullscreen-exit-outlined]" />
                            </el-icon>
                            {{ isFullScreen ? "退出全屏" : "全屏地图" }}
                        </el-dropdown-item>
                        <el-dropdown-item @click="adjustView()">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--reset-focus]" />
                            </el-icon>
                            缩放重置
                        </el-dropdown-item>
                        <el-dropdown-item @click="resetRotation()">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--rotate-right]" />
                            </el-icon>
                            旋转重置
                        </el-dropdown-item>
                        <el-dropdown-item divided @click="isMarking ? stopMarking() : startMarking()">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--location-on]" />
                            </el-icon>
                            <span :class="{ 'text-blue-500': isMarking }">
                                {{ isMarking ? '停止打点' : '地图打点' }}
                            </span>
                        </el-dropdown-item>
                        <el-dropdown-item @click="clearMarkers()">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--location-off-rounded]" />
                            </el-icon>
                            清除点位
                        </el-dropdown-item>
                        <el-dropdown-item
                            divided
                            @click="drawBoundary().then((geoJson) => {
                                ElMessage({
                                    message: `边界获取成功: ${JSON.stringify(geoJson).slice(0, 80)}...`,
                                    type: 'success'
                                })
                            })"
                        >
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--map]" />
                            </el-icon>
                            获取边界
                        </el-dropdown-item>
                        <el-dropdown-item @click="startMeasure('length')">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--straighten]" />
                            </el-icon>
                            <span :class="{ 'text-blue-500': measureType === 'length' }">
                                {{ measureType === 'length' ? '正在测距' : '测量距离' }}
                            </span>
                        </el-dropdown-item>
                        <el-dropdown-item @click="startMeasure('area')">
                            <el-icon class="el-icon--left">
                                <span class="icon-[material-symbols--calculate]" />
                            </el-icon>
                            <span :class="{ 'text-blue-500': measureType === 'area' }">
                                {{ measureType === 'area' ? '正在测面积' : '测量面积' }}
                            </span>
                        </el-dropdown-item>
                        <el-dropdown-item
                            @click="editMeasure()"
                        >
                            <el-icon class="el-icon--left">
                                <span class="icon-[icon-park-solid--edit]" />
                            </el-icon>
                            修改线条
                        </el-dropdown-item>
                        <el-dropdown-item
                            v-if="isMeasuring" @click="stopMeasure().then((geoJson) => {
                                if (geoJson) {
                                    ElMessage({
                                        message: `边界修改成功: ${JSON.stringify(geoJson).slice(0, 100)}...`,
                                        type: 'success'
                                    });
                                }
                            })"
                        >
                            <el-icon class="el-icon--left">
                                <span class="icon-[solar--stop-circle-bold]" />
                            </el-icon>
                            停止测量
                        </el-dropdown-item>
                        <el-dropdown-item @click="clearMeasure()">
                            <el-icon class="el-icon--left">
                                <span class="icon-[icon-park-solid--clear-format]" />
                            </el-icon>
                            清除测量
                        </el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </div>
</template>

<style scoped lang="scss">
@use "./style/control.scss";

.openLayersWrapper {
    position: fixed;
    top:0;
    left: 0;
    width: 100%;
    height: var(--enco-page-content-height);
}
</style>
